﻿#pragma once

#include <vulkan/vulkan.hpp>
#include "Render/Scene/PreRenderSceneNodes.h"
#include "Render/Scene/SceneParametersBuffer.h"
#include "Render/Scene/DefferRenderAttachments.h"

class CVulkanScene;
class CVulkanViewportWidget;
class CGPURenderTarget;

class CVulkanSpotLight
{
public:
    CVulkanSpotLight();
    ~CVulkanSpotLight();

    //设置聚光灯数据
    void SetSpotLight(const SPreRenderSpotLight& InSpotLight);

    //记录生成阴影图的命令
    void RecordShadowMapCommand(vk::CommandBuffer InCmdBuffers, CVulkanScene* InScene);

    //记录延迟着色的命令
    void RecordDefferShadingCommand(
        vk::CommandBuffer InCmdBuffers
        , CVulkanScene* InScene
        ,const SSceneParameters& InParameters 
        ,CDefferRenderAttachments* Attachments
        );

    int32_t LightIndex=-1;
protected:
    //调整阴影图大小，由于默认为0，所以至少要调用一次
    void Resize(uint32_t InWidth, uint32_t InHeight);



    uint32_t Width=0;
    uint32_t Height=0;
    SPreRenderSpotLight RenderData;

    std::shared_ptr<CGPURenderTarget> ShadowMap;
    std::shared_ptr<CGPURenderTarget> Depth; //渲染阴影贴图时的深度图  

    //用于渲染阴影贴图的缓冲区 
    std::shared_ptr<TVulkanUniformBuffer<CSceneGlobal::SSpotLightShadowMapBuffer>> ShadowMapBuffer;
    vk::Framebuffer ShadowMapFrameBuffer;
    vk::DescriptorSet ShadowMapDescriptorSet;

    //延迟着色描述符
    vk::DescriptorSet DefferShadingDescriptorSet=nullptr;

    SMatrix4x4 ProjectionViewMatrix;
};


//绘制聚光灯
class CVulkanSpotLights
{
public:
    CVulkanSpotLights()=default;

    //预处理聚光灯数据
    //筛选出在视锥体内的聚光灯
    //并统计描述符数量
    void PreProcess(const TVector<SPreRenderSpotLight>& InSpotLights,CVulkanViewportWidget* InViewport);

protected:

    //剔除后的聚光灯
    TVector<SPreRenderSpotLight> SpotLights;


public:
    //记录生成聚光灯阴影图的命令
    void RecordShadowMapCommand(
        vk::CommandBuffer InCmdBuffers
        ,CVulkanScene* InScene
        );

    //记录延迟着色的命令
    void RecordDefferShadingCommand(
        vk::CommandBuffer InCmdBuffers
        ,CVulkanScene* InScene
        ,const SSceneParameters& InParameters 
        ,CDefferRenderAttachments* Attachments
        
        );

    int64_t GetSpotLightNum() const
    {
        return RenderLights.Num();
    }

    //测试一个球体是否在光照范围内
    bool IsInLightRange(int64_t InLightIndex,const SVec3& InSpherePosition,float InSphereRadius);
protected:
    //聚光灯渲染对象，与聚光灯一一对应
    TVector<CVulkanSpotLight> RenderLights;

};